#region Using
using System;
using System.Drawing;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Threading;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using VB = Microsoft.VisualBasic;
using mshtml;
using Dimok;
using IE = Dimok.IE;
using Dimok.Data;
using Dimok.Exceptions;
#endregion

namespace Accurint {
	public class MainForm : System.Windows.Forms.Form,IAccurint {

		[STAThread]
		static void Main(string[] args) {
			Application.Run(new MainForm());
		}

		#region Disigner
		private System.Windows.Forms.Button btnLogin;
		private System.Windows.Forms.StatusBar stBar;
		private System.Windows.Forms.TextBox txtError;
		private System.Windows.Forms.Button tstPS;
		private System.Windows.Forms.Timer timerJerk;
		private System.Windows.Forms.Button btnInstantID;
		private System.Windows.Forms.Button button1;
		private Button btnPAW;
		private System.ComponentModel.IContainer components;
	
		public MainForm() {
			InitializeComponent();
			try{
				RemotingConfiguration.Configure(AppDomain.CurrentDomain.FriendlyName+".config");
				RemotingConfiguration.RegisterWellKnownServiceType(typeof(Server),Dimok.ConfigAccurint.Accurint.URL, WellKnownObjectMode.Singleton);
			}catch(Exception exc){
				MessageBox.Show(ClientException.GetExceptions(exc));
			}
			Session.form = this;
		}

		protected override void Dispose( bool disposing ) {
			if( disposing ) {
				if (components != null) {
					components.Dispose();
				}
			}
			if( _ie != null ){
				try{
					ie.Destroy();
					_ie = null;
				}catch{}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent() {
			this.components = new System.ComponentModel.Container();
			this.btnLogin = new System.Windows.Forms.Button();
			this.stBar = new System.Windows.Forms.StatusBar();
			this.txtError = new System.Windows.Forms.TextBox();
			this.tstPS = new System.Windows.Forms.Button();
			this.timerJerk = new System.Windows.Forms.Timer(this.components);
			this.btnInstantID = new System.Windows.Forms.Button();
			this.button1 = new System.Windows.Forms.Button();
			this.btnPAW = new System.Windows.Forms.Button();
			this.SuspendLayout();
			// 
			// btnLogin
			// 
			this.btnLogin.Location = new System.Drawing.Point(0, 0);
			this.btnLogin.Name = "btnLogin";
			this.btnLogin.Size = new System.Drawing.Size(75, 23);
			this.btnLogin.TabIndex = 0;
			this.btnLogin.Text = "Login";
			this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click);
			// 
			// stBar
			// 
			this.stBar.Location = new System.Drawing.Point(0, 450);
			this.stBar.Name = "stBar";
			this.stBar.Size = new System.Drawing.Size(612, 22);
			this.stBar.TabIndex = 1;
			// 
			// txtError
			// 
			this.txtError.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
									| System.Windows.Forms.AnchorStyles.Left)
									| System.Windows.Forms.AnchorStyles.Right)));
			this.txtError.Font = new System.Drawing.Font("Courier New", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
			this.txtError.Location = new System.Drawing.Point(0, 24);
			this.txtError.Multiline = true;
			this.txtError.Name = "txtError";
			this.txtError.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
			this.txtError.Size = new System.Drawing.Size(612, 424);
			this.txtError.TabIndex = 2;
			// 
			// tstPS
			// 
			this.tstPS.Location = new System.Drawing.Point(80, 0);
			this.tstPS.Name = "tstPS";
			this.tstPS.Size = new System.Drawing.Size(78, 23);
			this.tstPS.TabIndex = 3;
			this.tstPS.Text = "People Search";
			this.tstPS.Click += new System.EventHandler(this.tstPS_Click);
			// 
			// timerJerk
			// 
			this.timerJerk.Enabled = true;
			this.timerJerk.Interval = 1000;
			this.timerJerk.Tick += new System.EventHandler(this.timerJerk_Tick);
			// 
			// btnInstantID
			// 
			this.btnInstantID.Location = new System.Drawing.Point(164, 0);
			this.btnInstantID.Name = "btnInstantID";
			this.btnInstantID.Size = new System.Drawing.Size(75, 23);
			this.btnInstantID.TabIndex = 3;
			this.btnInstantID.Text = "Instant ID";
			this.btnInstantID.Click += new System.EventHandler(this.btnInstantID_Click);
			// 
			// button1
			// 
			this.button1.Location = new System.Drawing.Point(244, 0);
			this.button1.Name = "button1";
			this.button1.Size = new System.Drawing.Size(75, 23);
			this.button1.TabIndex = 3;
			this.button1.Text = "DL";
			this.button1.Click += new System.EventHandler(this.button1_Click);
			// 
			// btnPAW
			// 
			this.btnPAW.Location = new System.Drawing.Point(325, 0);
			this.btnPAW.Name = "btnPAW";
			this.btnPAW.Size = new System.Drawing.Size(96, 23);
			this.btnPAW.TabIndex = 3;
			this.btnPAW.Text = "People At Work";
			this.btnPAW.Click += new System.EventHandler(this.btnPAW_Click);
			// 
			// MainForm
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(612, 472);
			this.Controls.Add(this.btnPAW);
			this.Controls.Add(this.tstPS);
			this.Controls.Add(this.txtError);
			this.Controls.Add(this.stBar);
			this.Controls.Add(this.btnLogin);
			this.Controls.Add(this.btnInstantID);
			this.Controls.Add(this.button1);
			this.Name = "MainForm";
			this.Text = "Accurint Jerker";
			this.Load += new System.EventHandler(this.MainForm_Load);
			this.ResumeLayout(false);
			this.PerformLayout();

		}
		#endregion

		#endregion
		
		#region Properties/Fields
		private IE.IE _ie = null;
		private string _sessionURL = "";
		private string SessionURL{
			get{ return _sessionURL == "" ? Dimok.Config.LogIn : _sessionURL; }
			set{ _sessionURL =value; }
		}

		public IE.IE ie{
			get{
				if( _ie == null ){
					_ie = new Dimok.IE.IE();
				}
				try{
					object o = _ie.doc;
				}catch(Exception exc){
					Error = "Get(IE):"+exc.Message;
					try{_ie.Destroy();}catch{}
					_ie = new Dimok.IE.IE();
				}
				return _ie;
			}
		}
		private string Status{set{stBar.Text = value;Application.DoEvents();}}
		private string Error{
			set{
				this.Activate();
				Application.DoEvents();	txtError.Focus();
				txtError.Text += Environment.NewLine + value;
				txtError.SelectAll();
				Application.DoEvents();txtError.ScrollToCaret();Application.DoEvents();
			}
		}
		private bool InCall = false;
		private Random RandomTimer = new Random(DateTime.Now.Millisecond);
		#endregion

		#region Remote Inteface

		#region InstantID
		private void FillInstantIDRequest(string firstName,string lastName,string SSN,string street,string city,string state,string zip,string DOB,string homePhone,string driverLicense){
			SSN = (SSN+"").Trim();
			if( SSN != null && SSN != "" && 
				!Regex.IsMatch(SSN,@"\d\d\d-\d\d-\d\d\d\d") &&
				!Regex.IsMatch(SSN,@"\d\d\d\d\d\d\d\d\d")
				)throw new Exception("SSN is incorrect["+SSN+"]!");
			ie.SetElementValue("SSN",SSN);
			ie.SetElementValue("FIRST_NAME",firstName+"");
			ie.SetElementValue("LAST_NAME",lastName+"");
			ie.SetElementValue("STREET_ADDRESS",street+"");
			ie.SetElementValue("CITY",city+"");
			ie.SetElementValue("STATE",state+"");
			ie.SetElementValue("ZIP",Regex.Replace(zip+"","[^\\d]",""));
			ie.SetElementValue("PHONE",Regex.Replace(homePhone+"","[^\\d]",""));
			ie.SetElementValue("DOB",DOB+"");
			ie.SetElementValue("LICENSE_NUMBER",driverLicense+"");
			ie.GetElementByAttr("value","Report").click();
			ie.WaitIE();
		}
		public int FindInstantIDResult(IE.IE ie){
			string retImg = "";
			foreach(IHTMLImgElement img in ie.doc.images)
				if( Regex.IsMatch(img.src,"caution") )
					if((retImg = Regex.Match(img.src,@"(\d+).{4}",RegexOptions.RightToLeft).Groups[1].Value) != "")break;
			string retTD = ie.GetElementByAttr("className","preload").innerText.Trim();
			if( retImg != retTD  || retImg == "" )throw new ApplicationException("InstantID HTML has changed.");
			return int.Parse(retImg);
		}
		public PersonDS GoInstantID(string firstName,string lastName,string SSN,string street,string city,string state,string zip,string DOB,string homePhone,string driverLicense){
			try{
				InCall = true;
				PersonDS dsInstID = new Accurint.PersonDS();
				int score = -1;
				string html = "";
				try{
					NavigateToInstantID();
					ie.WaitIE();
					FillInstantIDRequest(firstName,lastName,SSN,street,city,state,zip,DOB,homePhone,driverLicense);
					long ticks = DateTime.Now.AddMilliseconds(10000).Ticks;
					while( ticks > DateTime.Now.Ticks && !ie.HasPopUp )Application.DoEvents();
					if( !ie.HasPopUp )throw new Exception("No PopUp found!");
					ie.PopUp.WaitIE(10000);
					score = FindInstantIDResult(ie.PopUp);
					html = IE.IE.InjectBASE(ie.PopUp.doc.body.parentElement.innerHTML,ie.PopUp.doc.url);
				}catch(Exception exc){
					ClientException.Publish(exc);
					html = ClientException.GetExceptions(exc);
				}
				dsInstID.InstantID.AddInstantIDRow(score,html);
				return dsInstID;
			}catch(Exception exc){
				Error = ClientException.GetExceptions(exc);
				throw new ClientException(exc);
			}finally{	
				try{ie.PopUp.Destroy();	ie.PopUp = null;}catch{}
				InCall = false;
			}
		}
		#endregion

		#region PeopleSearch
		public PersonDS GoPeopleSearch(string SSN){
			try{
				InCall = true;
				try{
					NavigateToSearch();
				}catch(Exception exc){
					ClientException.Publish(exc);
					Error = ClientException.GetMessages(exc);
					throw;
				}
				FillSearchRequest(SSN);
				MatchCollection mcNames = ParseTextResults(ie.doc.body.innerText);
				GroupCollection gcNames = GetFullName(mcNames);
				PersonDS dsPerson = new PersonDS();
				dsPerson.Info.AddInfoRow(gcNames["first"].Value,gcNames["last"].Value,gcNames["middle"].Value,gcNames["dob"].Value,gcNames["address"].Value,ie.doc.body.innerText);
				return dsPerson;
			}finally{
				InCall = false;
			}
		}
		private GroupCollection GetFullName(MatchCollection mcNames){
			string[] sNames = new string[0];
			string re = Regex.IsMatch(mcNames[0].Value,"\\u2550") ?
				@"Names.+?-\s+(?:(?<last>.+?),\u2550(?<first>[^\u2550]+)\u2550(?<middle>\w*)\u2550?)" :
				@"Names.+?-\s+(?:(?<last>.+?),[ ]*(?<first>(\S{2,}\s)+)(?<middle>\S?)?\s+)+SSN";
			//@"(?<=[*]+\s+)Names.+\s+(?:(?<last>.+?),\u2550(?<first>.+?)\u2550(?<middle>\w*).+\s+)+?SSN.+\s+(?:(?<ssn>.+?)\s+)+?DOB.+\s+(?:(?<dob>.*?)\s+)+?Address\s*-\s*(?<street>.*,?),\u2550(?<city>.+?)\u2550(?<state>\w*)\u2550(?<zip>\S*)\s+[\s\S]+?(?=[*]+)";
			foreach( Match mNames in mcNames ){
				Match mName = Regex.Match(Regex.Escape(mNames.Value),re);
				if( mName.Groups["middle"].Captures.Count > 0 &&
					mName.Groups["middle"].Captures[0].Value != "" )
					return mName.Groups;
			}
			return Regex.Match(mcNames[0].Value,re).Groups;
		}
		private MatchCollection ParseTextResults(string text){
			const string re = @"(?<=[*]+\s+)[\s\S]+?(?=[*]+)";
			FileManager.GetFileFromBytes(text,"C:\\RegEx.txt",false);
			return Regex.Matches(text,re,RegexOptions.Compiled);
		}
		private void FillSearchRequest(string SSN){FillSearchRequest(SSN,"","","","");}
		private void FillSearchRequest(string SSN,string firstName,string lastName,string middleName,string state){
			if( SSN != null && SSN != "" && 
				!Regex.IsMatch(SSN,@"\d\d\d-\d\d-\d\d\d\d") &&
				!Regex.IsMatch(SSN,@"\d\d\d\d\d\d\d\d\d")
				)throw new Exception("SSN is incorrect["+SSN+"]!");
			ie.SetElementValue("SSN",SSN);
			ie.SetElementValue("FIRST_NAME",firstName);
			ie.SetElementValue("LAST_NAME",firstName);
			ie.SetElementValue("MI",middleName);
			ie.SetElementValue("STATE",state);
			ie.SetElementValue("RESULTS_TYPE","text");
			ie.SetElementValue("RESULTS_TYPE","text");
			if( !ie.GoToLinkByHRef("javascript:send_event('SEARCH/SEARCH')",false) )
				ie.GoToLinkByAttr("value","^Search$",true);
			if( Regex.IsMatch(ie.doc.body.innerText,"Too many records found.",RegexOptions.Compiled | RegexOptions.IgnoreCase) )
				throw new ToManyRecordsException();
		}
		#endregion

		#region DriverLicense
		public string GoDriverLicense(string driverLicense){
			try{
				InCall = true;
				try{
					NavigateToDriverLicense();
				}catch(Exception exc){
					ClientException.Publish(exc);
					Error = ClientException.GetMessages(exc);
					throw;
				}
				FillDriverLicenseRequest(driverLicense);
				return ie.GetElementByName("SEARCH_RESULTS").innerHTML;
			}finally{
				InCall = false;
			}
		}
		private void FillDriverLicenseRequest(string driverLicense){
			ie.SetElementValue("LICENSE_NUMBER",driverLicense);
			ie.SetElementValue("RESULTS_TYPE","text");
			ie.GetElementByAttr("value","^Search$").click();
			ie.WaitIE(false);
		}
		#endregion

		public PersonDS GoPeopleAtWork(string SSN) {
			try {
				InCall = true;
				try {
					NavigateToPAW();
				} catch (Exception exc) {
					ClientException.Publish(exc);
					Error = ClientException.GetMessages(exc);
					throw;
				}
				FillSearchRequest(SSN);
				PersonDS dsPerson = new PersonDS();
				dsPerson.Info.AddInfoRow("", "", "", "", "", ie.doc.body.innerText);
				return dsPerson;
			} finally {
				InCall = false;
			}
		}

		#region Navigate
		public void Login(){
			bool inCall = InCall;
			InCall = true;
			try{
				Status = "Logging In ...";
				string sessionUrl = Regex.Match(ie.doc.url,"SESSION_ID=(?<sid>.+?)&",RegexOptions.IgnoreCase).Groups["sid"].Value;
				if( sessionUrl != "" )SessionURL = ie.doc.url;
				HTMLInputElementClass ce = ie.GetElementByName("CURRENT_EVENT") as HTMLInputElementClass;
				if( ce != null ){
					if( ce.value.IndexOf("LOGIN")>=0 ){
						IE.IE.SetElementValue(ce.form,"EVENT","SEARCH/SAVE_DPPA");
						ce.form.submit();
						ie.WaitIE(false);
						ClickTrough();
					}else{
						foreach (IHTMLInputElement el in IE.IE.Tags(ie.docClass.body, "INPUT")) 
							if( el.type.ToLower() == "text" ) el.value = "";
						IE.IE.SetElementValue(ce.form,"EVENT",ce.value);
						ce.form.submit();
						ie.WaitIE(false);
					}
					return;
				}

				ie.Navigate(SessionURL);
				if( ie.GetElementByName("LOGINID") != null ){
					IHTMLElement2 body = (IHTMLElement2)ie.doc.body;
					IHTMLControlRange range = (IHTMLControlRange)body.createControlRange();
					IHTMLImgElement elImg = (IHTMLImgElement)ie.GetElementByName("tokenimg");
					while(elImg.readyState != "complete")Application.DoEvents();
					range.add((IHTMLControlElement)elImg);
					range.execCommand("copy",false,null);
					//Atalasoft.Imaging.WebControls.WorkspaceViewer vw = new Atalasoft.Imaging.WebControls.WorkspaceViewer();
					Atalasoft.Imaging.AtalaImage img = Atalasoft.Imaging.AtalaImage.ImageFromClipboard(this.Handle);
					img.Save("C:\\Atala.gif",Atalasoft.Imaging.Codec.ImageType.Gif,null);
					img.Dispose();
					ie.SetElementValue("LOGINID",Dimok.Data.Registry.AppSettings("UserName"));
					ie.SetElementValue("PASSWORD",Dimok.Data.Registry.AppSettings("Password"));
					ie.SetElementValue("checkboxLow",true);
					((mshtml.HTMLInputTextElement)ie.GetElementByName("_T")).focus();
					throw new NeedLoginException();
				}
				Status = "Active";
			}finally{
				InCall = inCall;
			}
		}
		private void NavigateToDriverLicense(){
			if( ie.doc.title == "Driver Licenses Search" )return;
			GoToEvent("SEARCH/SHOW_LICENSE");
		}
		private void NavigateToSearch(){
			if( ie.doc.title == "Person Search" )return;
			GoToEvent("SEARCH/SHOW_SEARCH");
		}
		private void NavigateToInstantID(){
			if( ie.doc.title == "Instant ID" )return;
			GoToEvent("SEARCH/SHOW_INSTANT_ID");
		}
		private void NavigateToPAW() {
			if (ie.doc.title == "People At Work") return;
			GoToEvent("SEARCH/SHOW_PAW");
		}
		private void GoToEvent(string eventName) {
			HTMLInputElementClass ce = ie.GetElementByName("CURRENT_EVENT") as HTMLInputElementClass;
			if( ce == null )throw new Exception("No form element found!");
			IE.IE.SetElementValue(ce.form,"EVENT",eventName);
			ce.form.submit();
			ie.WaitIE(false);
			ClickTrough();
		}
		private void ClickTrough(){
			ie.GoToLinkByText("Click to Continue Transactions Authorized by Consumer",false);
			ie.GoToLinkByText("Click to Continue Use in the Normal Course of Business",false);
		}
		private void NavigateTo(string linkText){
			Login();
			ie.WaitIE();
			ie.GoToLinkByHRef("javascript:send_event('SEARCH/SHOW_MENU')",true);
			ie.GoToLinkByText(linkText,true);
			ie.WaitIE();
			ClickTrough();
		}
		#endregion

		#endregion

		#region Events
		private void btnLogin_Click(object sender, System.EventArgs e) {
			Login();
		}

		private void tstPS_Click(object sender, System.EventArgs e) {
			try{
				txtError.Text = GoPeopleSearch("358704124").GetXml();
			}catch(Exception exc){
				ClientException.Publish(exc);
				Error = ClientException.GetMessages(exc);
			}
		}
		private void btnPAW_Click(object sender, EventArgs e) {
			try {
				txtError.Text = GoPeopleAtWork("358704124").GetXml();
			} catch (Exception exc) {
				ClientException.Publish(exc);
				Error = ClientException.GetMessages(exc);
			}
		}
		private void btnInstantID_Click(object sender, System.EventArgs e) {
			try{
				txtError.Text = GoInstantID("dmitri","lapchine","578232412","3151 ne 212","Aventura","fl","33180","8/17/1962","3057880763","").GetXml();
			}catch(Exception exc){
				ClientException.Publish(exc);
				Error = ClientException.GetExceptions(exc);
			}
		}

		private void timerJerk_Tick(object sender, System.EventArgs e) {
			if( InCall )return;
			int i = RandomTimer.Next(-Dimok.Config.JerkTimeOut/2,Dimok.Config.JerkTimeOut/2);
			timerJerk.Interval = Dimok.Config.JerkTimeOut + i;
			try{
				Login();
			}catch(NeedLoginException){
			}catch(Exception exc){
				Error = ClientException.GetExceptions(exc);
			}
		}

		private void MainForm_Load(object sender, System.EventArgs e) {
		}
		#endregion

		private void button1_Click(object sender, System.EventArgs e) {
			try{
				txtError.Text = GoDriverLicense("L125160622");
			}catch(Exception exc){
				ClientException.Publish(exc);
				Error = ClientException.GetExceptions(exc);
			}
		}

		private class ToManyRecordsException : Exception{}

	}
}
